package jmine.tec.aom.util;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import jmine.tec.aom.domain.AbstractPropertyValue;
import jmine.tec.aom.domain.Propertized;
import jmine.tec.aom.domain.Property;

/**
 * Handles dynamic {@link AbstractPropertyValue} given a target {@link Propertized}.
 * 
 * @author jrenaut
 */
public class BasePropertyHandler implements Propertized {

    private final Set<Property> dynamicProperties;

    private final Set<AbstractPropertyValue<?>> dynamicPropertyValues;

    private final Propertized parent;

    /**
     * @param dynamicProperties the {@link Set} of {@link Property} to be managed
     * @param dynamicPropertyValues the {@link Set} of {@link AbstractPropertyValue} to be managed
     */
    public BasePropertyHandler(Set<Property> dynamicProperties, Set<AbstractPropertyValue<?>> dynamicPropertyValues) {
        this(null, dynamicProperties, dynamicPropertyValues);
    }

    /**
     * @param parent the parent {@link Propertized}
     * @param dynamicPropertyValues the {@link Set} of {@link AbstractPropertyValue} to be managed
     */
    public BasePropertyHandler(Propertized parent, Set<AbstractPropertyValue<?>> dynamicPropertyValues) {
        this(parent, new HashSet<Property>(), dynamicPropertyValues);
    }

    /**
     * @param parent the parent {@link Propertized}
     * @param dynamicProperties the {@link Set} of {@link Property} to be managed
     * @param dynamicPropertyValues the {@link Set} of {@link AbstractPropertyValue} to be managed
     */
    private BasePropertyHandler(Propertized parent, Set<Property> dynamicProperties, Set<AbstractPropertyValue<?>> dynamicPropertyValues) {
        this.parent = parent;
        this.dynamicProperties = dynamicProperties;
        this.dynamicPropertyValues = dynamicPropertyValues;
    }

    /**
     * {@inheritDoc}
     */
    public Set<Property> getProperties() {
        Set<Property> props = this.getDynamicProperties();
        return Collections.unmodifiableSet(props != null ? new HashSet<Property>(props) : new HashSet<Property>());
    }

    /**
     * {@inheritDoc}
     */
    public Set<AbstractPropertyValue<?>> getPropertyValues() {
        return Collections.unmodifiableSet(this.dynamicPropertyValues);
    }

    /**
     * {@inheritDoc}
     */
    public Propertized getParent() {
        return this.parent;
    }

    /**
     * {@inheritDoc}
     */
    public Property getPropertyByName(String name) {
        for (Property prop : this.getDynamicProperties()) {
            if (prop.getName().equalsIgnoreCase(name)) {
                return prop;
            }
        }
        return null;
    }

    /**
     * {@inheritDoc}
     */
    public Property getPropertyByMnemonic(String mnemonic) {
        for (Property prop : this.getDynamicProperties()) {
            if (prop.getMnemonic().equalsIgnoreCase(mnemonic)) {
                return prop;
            }
        }
        return null;
    }

    /**
     * {@inheritDoc}
     */
    public AbstractPropertyValue getPropertyValue(Property property) {
        if (property != null) {
            for (AbstractPropertyValue apv : this.dynamicPropertyValues) {
                if (apv.getProperty().equals(property)) {
                    return apv;
                }
            }
        }
        return null;
    }

    /**
     * {@inheritDoc}
     */
    public void removePropertyValue(AbstractPropertyValue propertyValue) {
        this.dynamicPropertyValues.remove(propertyValue);
    }

    /**
     * {@inheritDoc}
     */
    public void addPropertyValue(AbstractPropertyValue propertyValue) {
        if (!propertyValue.isSynthetic()) {
            AbstractPropertyValue currentValue = this.getPropertyValue(propertyValue.getProperty());
            if (currentValue != null) {
                this.dynamicPropertyValues.remove(currentValue);
            }
            this.dynamicPropertyValues.add(propertyValue);
        }
    }

    /**
     * @return a {@link Set} of {@link Property}
     */
    private Set<Property> getDynamicProperties() {
        if (this.getParent() != null) {
            return this.getParent().getProperties();
        } else {
            return this.dynamicProperties;
        }
    }

    /**
     * {@inheritDoc}
     */
    public Set<Property> getRealProperties() {
        return this.getProperties();
    }

    /**
     * {@inheritDoc}
     */
    public Set<Property> getSyntheticProperties() {
        return Collections.emptySet();
    }

}
